import { AppStyleCollections, logger } from '../../../types/constants'
import { ToolKit } from '../../utils/toolkit'

/**
 * Iframe 操作配置
 */
export interface IframeOperationConfig {
  url: string
  timeout?: number
  iframeClass?: string
  iframeStyle?: Partial<CSSStyleDeclaration>
  retryCount?: number
  retryDelay?: number
}

/**
 * Iframe 操作结果
 */
export interface IframeOperationResult<T = any> {
  success: boolean
  data?: T
  error?: Error
  duration: number
  retryCount: number
  metadata?: any
}

/**
 * Iframe 选择器配置
 */
export interface IframeSelectorConfig {
  downloadButton?: string
  size?: string
  bulk?: string
  [key: string]: string | undefined
}

/**
 * 默认 Iframe 操作配置
 */
const DEFAULT_IFRAME_OPERATION_CONFIG: Required<IframeOperationConfig> = {
  url: '',
  timeout: 5000,
  iframeClass: 'iframe-operator-iframe',
  iframeStyle: {},
  retryCount: 2,
  retryDelay: 1000,
}

/**
 * 通用 Iframe 操作器
 */
export class IframeOperator {
  private readonly name: string = 'IframeOperator'
  private static instance: IframeOperator

  public static getInstance(): IframeOperator {
    if (!IframeOperator.instance) {
      IframeOperator.instance = new IframeOperator()
    }
    return IframeOperator.instance
  }

  /**
   * 执行 iframe 操作
   */
  public async executeOperation<T>(
    config: IframeOperationConfig,
    operation: ($iframeDoc: JQuery<Document>) => Promise<T>,
    operationType: string = 'customOperation',
  ): Promise<IframeOperationResult<T>> {
    const mergedConfig = ToolKit.config.mergeConfig(DEFAULT_IFRAME_OPERATION_CONFIG, config)

    logger.debug(`[${this.name}] 配置信息`, mergedConfig)

    const { url, timeout, retryCount: maxRetries, retryDelay } = mergedConfig
    const shortenUrl = ToolKit.browser.url.shortenUrl(url)

    const startTime = Date.now()
    let retryCount = 0

    while (retryCount <= maxRetries) {
      try {
        const result = await this.executeSingleOperation(mergedConfig, operation, timeout)
        const duration = Date.now() - startTime

        logger.debug(`[${this.name}] ${operationType} 操作成功`, {
          url: shortenUrl,
          duration: `${duration} ms`,
          retryCount,
          operationType,
        })

        return {
          success: true,
          data: result,
          duration,
          retryCount,
        }
      }
      catch (error) {
        retryCount++

        if (retryCount > maxRetries) {
          const duration = Date.now() - startTime

          logger.warn(`[${this.name}] ${operationType} 操作失败`, {
            url: shortenUrl,
            error: error instanceof Error ? error.message : String(error),
            duration: `${duration}ms`,
            retryCount: maxRetries,
            operationType,
          })

          return {
            success: false,
            error: error instanceof Error ? error : new Error(String(error)),
            duration,
            retryCount: maxRetries,
          }
        }

        logger.error(`[${this.name}] ${operationType} 操作重试`, {
          url: shortenUrl,
          retryCount,
          maxRetries,
          delay: `${retryDelay}ms`,
          operationType,
        })

        await ToolKit.async.delay(retryDelay)
      }
    }

    // 理论上不会执行到这里
    return {
      success: false,
      error: new Error('操作失败'),
      duration: Date.now() - startTime,
      retryCount,
    }
  }

  /**
   * 执行单次操作
   */
  private async executeSingleOperation<T>(
    config: IframeOperationConfig,
    operation: ($iframeDoc: JQuery<Document>) => Promise<T>,
    timeout: number,
  ): Promise<T> {
    const { url } = config

    return new Promise<T>((resolve, reject) => {
      const $iframe = this.createHiddenIframe(config)

      let timeoutId: number
      let isResolved = false

      // 清理函数
      const cleanup = () => {
        if (isResolved)
          return

        isResolved = true
        clearTimeout(timeoutId)
        // 移除 iframe 元素
        $iframe.remove()
      }

      // 成功处理函数
      const handleSuccess = (result: T) => {
        cleanup()
        resolve(result)
      }

      // 错误处理函数
      const handleError = (error: Error) => {
        cleanup()
        reject(error)
      }

      // 设置超时
      timeoutId = window.setTimeout(() => {
        handleError(new Error(`操作超时 (${timeout} ms)`))
      }, timeout)

      $iframe.on('load', async () => {
        try {
          const iframeDoc = ($iframe[0] as HTMLIFrameElement).contentDocument
            || ($iframe[0] as HTMLIFrameElement).contentWindow?.document

          if (!iframeDoc) {
            handleError(new Error('无法访问 iframe 内容'))
            return
          }

          const $iframeDoc = $(iframeDoc)
          const result = await operation($iframeDoc)

          handleSuccess(result)
        }
        catch (error) {
          handleError(error instanceof Error ? error : new Error(String(error)))
        }
      })

      $iframe.on('error', () => {
        handleError(new Error('加载 iframe 失败'))
      })

      // 加载页面
      $iframe.attr('src', url)
      $('body').append($iframe)
    })
  }

  /**
   * 创建隐藏的 iframe
   */
  private createHiddenIframe(config: IframeOperationConfig): JQuery<HTMLIFrameElement> {
    const iframeClass = config.iframeClass
    const iframeStyle = { ...config.iframeStyle }
    const $iframe = $(`<iframe class="${iframeClass} ${AppStyleCollections.Body.PAGE_IFRAME}" />`)

    Object.assign($iframe[0].style, iframeStyle)
    ToolKit.browser.platform.markProjectElement($iframe)

    return $iframe as JQuery<HTMLIFrameElement>
  }
}
